const fs = require('fs').promises;
const fsSync = require('fs');
const path = require('path');
const {Printer} = require('../models/printer');
const {existsSync} = require('fs');
const {exec} = require('child_process');
const util = require('util');
const {promisify} = require('util');
const execPromise = util.promisify(exec);

// 导入Word转换相关库
let mammoth;
try {
  mammoth = require('mammoth');
} catch (err) {
  console.warn('mammoth库未安装，无法使用Word转HTML功能');
}

class PrintService {
    static async printDocument(printer, file, task) {
        try {
            // 检查参数
            if (!printer || !file || !task) {
                throw new Error('缺少必要的打印参数');
            }

            console.log('======= 开始打印文档 =======');
            console.log('打印参数:', JSON.stringify({
                '打印机': printer.name,
                '打印机IP': printer.ip,
                '打印机端口': printer.port,
                '文件': file.originalName,
                '文件类型': file.type,
                '文件大小': fsSync.statSync(file.path).size + ' 字节',
                '任务ID': task.id,
                '选择的格式': task.format,
                '打印份数': task.copies || 1
            }, null, 2));

            // 检查文件是否存在
            try {
                await fs.access(file.path);
                console.log('文件存在，可以访问');
            } catch (err) {
                throw new Error(`文件不存在或无法访问: ${file.path}`);
            }

            // 检查文件类型并按需转换
            let fileToProcess = file.path;
            const fileExt = path.extname(file.path).toLowerCase();
            let tempFile = null;

            if (fileExt === '.docx' || fileExt === '.doc') {
                console.log('检测到Word文档，先转换为PDF再打印...');
                
                // 创建临时目录
                const tempDir = path.join(process.cwd(), 'temp', `convert_${Date.now()}`);
                await fs.mkdir(tempDir, { recursive: true });
                
                // 转换Word为PDF
                tempFile = path.join(tempDir, `${path.basename(file.path, fileExt)}.pdf`);
                
                try {
                    console.log(`开始转换Word文件为PDF: ${file.path} -> ${tempFile}`);
                    
                    // 转换Word为HTML (使用mammoth库)
                    if (mammoth) {
                        // 1. 读取Word文档
                        console.log('使用mammoth转换Word为HTML...');
                        const buffer = await fs.readFile(file.path);
                        
                        // 2. 使用mammoth将Word转为HTML
                        const result = await mammoth.convertToHtml({ buffer });
                        const htmlContent = result.value;
                        
                        // 将HTML保存到临时文件
                        const tempHtmlFile = path.join(tempDir, `${path.basename(file.path, fileExt)}.html`);
                        await fs.writeFile(tempHtmlFile, 
                          `<!DOCTYPE html>
                          <html>
                          <head>
                            <meta charset="utf-8">
                            <title>转换文档</title>
                            <style>
                              body { font-family: Arial, sans-serif; margin: 40px; }
                              table { border-collapse: collapse; width: 100%; }
                              th, td { border: 1px solid #ddd; padding: 8px; }
                              th { background-color: #f2f2f2; }
                              img { max-width: 100%; }
                            </style>
                          </head>
                          <body>
                            ${htmlContent}
                          </body>
                          </html>`
                        );
                        
                        // 3. 使用wkhtmltopdf命令行工具将HTML转为PDF
                        console.log('使用wkhtmltopdf将HTML转为PDF...');
                        const wkhtmlCmd = `wkhtmltopdf "${tempHtmlFile}" "${tempFile}"`;
                        try {
                          await execPromise(wkhtmlCmd);
                          
                          if (existsSync(tempFile)) {
                            console.log('HTML成功转换为PDF');
                            fileToProcess = tempFile;
                          } else {
                            throw new Error('wkhtmltopdf转换失败，PDF文件未生成');
                          }
                        } catch (wkErr) {
                          console.warn('wkhtmltopdf转换失败，尝试使用PowerShell和IE引擎转换...');
                          
                          // 如果wkhtmltopdf失败，尝试使用PowerShell和IE引擎
                          const psSaveAsPDF = `
# PowerShell脚本：将HTML转为PDF
$htmlFile = "${tempHtmlFile.replace(/\\/g, '\\\\')}"
$pdfFile = "${tempFile.replace(/\\/g, '\\\\')}"

# 使用IE引擎和COM对象转换HTML到PDF
try {
    # 创建Word应用程序对象（可选方法，需要Word安装）
    $word = New-Object -ComObject Word.Application
    $word.Visible = $false
    
    # 创建一个新文档
    $doc = $word.Documents.Add()
    
    # 读取HTML内容
    $htmlContent = Get-Content -Path $htmlFile -Raw
    
    # 将HTML内容插入到Word文档
    $doc.Application.Selection.InsertHTML($htmlContent)
    
    # 另存为PDF
    $pdfFormat = 17  # wdFormatPDF常量
    $doc.SaveAs([ref] $pdfFile, [ref] $pdfFormat)
    
    # 关闭文档
    $doc.Close()
    $word.Quit()
    
    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($word) | Out-Null
    
    Write-Output "HTML转PDF成功: $pdfFile"
} catch {
    Write-Output "转换错误: $_"
    exit 1
}
`;
                          // 将脚本写入临时文件
                          const psScriptPath = path.join(tempDir, 'html_to_pdf.ps1');
                          await fs.writeFile(psScriptPath, psSaveAsPDF);
                          
                          // 执行PowerShell脚本
                          try {
                            await execPromise(`powershell.exe -ExecutionPolicy Bypass -File "${psScriptPath}"`);
                            
                            if (existsSync(tempFile)) {
                              console.log('HTML使用PowerShell成功转换为PDF');
                              fileToProcess = tempFile;
                            } else {
                              throw new Error('PowerShell转换失败');
                            }
                          } catch (psErr) {
                            // 如果两种方法都失败，尝试使用备选方法（直接打印Word文档）
                            console.error('所有HTML转PDF方法都失败，尝试使用系统打印功能...');
                            const success = await this.convertWithExternalTool(file.path, tempFile);
                            
                            if (success && existsSync(tempFile)) {
                              console.log('使用系统工具转换成功');
                              fileToProcess = tempFile;
                            } else {
                              console.warn('所有转换方法均失败，将尝试直接打印原始文件');
                            }
                          }
                        }
                      } else {
                        // mammoth库不可用，使用备选方法
                        console.warn('mammoth库不可用，使用备选方法转换Word文档');
                        const success = await this.convertWithExternalTool(file.path, tempFile);
                        
                        if (success && existsSync(tempFile)) {
                          console.log('使用备选方法转换成功');
                          fileToProcess = tempFile;
                        }
                      }
                } catch (convError) {
                    console.error('Word转PDF失败:', convError);
                    
                    // 尝试使用系统工具
                    console.log('尝试使用系统工具转换Word文档...');
                    const success = await this.convertWithExternalTool(file.path, tempFile);
                    
                    if (success && existsSync(tempFile)) {
                        console.log('使用系统工具转换成功');
                        fileToProcess = tempFile;
                    } else {
                        console.warn('所有转换方法均失败，将尝试直接打印原始文件');
                    }
                }
            }

            // 直接使用打印方法
            console.log(`开始打印文件: ${fileToProcess} 到打印机: ${printer.name}`);
            const result = await this.directPrint(fileToProcess, printer.name, {
                copies: task.copies || 1
            });

            console.log('打印任务发送结果:', result.message);

            // 清理临时文件
            if (tempFile && existsSync(tempFile)) {
                try {
                    await fs.unlink(tempFile);
                    console.log('临时PDF文件已删除');
                    
                    // 尝试删除临时目录
                    const tempDir = path.dirname(tempFile);
                    if (existsSync(tempDir)) {
                        await fs.rmdir(tempDir, { recursive: true });
                        console.log('临时目录已删除');
                    }
                } catch (cleanupError) {
                    console.warn('清理临时文件失败:', cleanupError.message);
                }
            }

            // 更新任务状态
            await task.update({status: 'completed'});
            console.log('打印任务标记为完成');
            console.log('======= 打印流程结束 =======');
            return {success: true, message: result.message};
        } catch (error) {
            console.error('======= 打印失败 =======');
            console.error('错误详情:', error);
            if (task) {
                await task.update({
                    status: 'failed',
                    errorMessage: error.message || '未知错误'
                });
                console.log('任务状态已更新为失败');
            }
            throw error;
        }
    }

    // 使用外部工具转换Word文档为PDF
    static async convertWithExternalTool(inputFile, outputFile) {
        try {
            console.log('尝试使用外部命令行工具转换Word文档...');
            
            // 检查系统是否有LibreOffice
            const libreOfficeCmd = `soffice --headless --convert-to pdf --outdir "${path.dirname(outputFile)}" "${inputFile}"`;
            
            try {
                console.log('尝试使用LibreOffice转换...');
                await execPromise(libreOfficeCmd);
                // LibreOffice会生成一个与输入文件同名但扩展名为PDF的文件
                const generatedPdfPath = path.join(
                    path.dirname(outputFile), 
                    `${path.basename(inputFile, path.extname(inputFile))}.pdf`
                );
                
                // 如果生成的文件路径与预期输出路径不同，需要重命名
                if (generatedPdfPath !== outputFile && existsSync(generatedPdfPath)) {
                    await fs.rename(generatedPdfPath, outputFile);
                }
                
                if (existsSync(outputFile)) {
                    console.log('使用LibreOffice转换成功');
                    return true;
                }
            } catch (libreOfficeError) {
                console.warn('LibreOffice转换失败:', libreOfficeError.message);
            }
            
            // 尝试使用Office COM对象（仅在Windows上有效）
            console.log('尝试使用Office COM对象转换...');
            
            // 创建PowerShell脚本内容
            const psScriptContent = `
# PowerShell脚本：转换Word为PDF
$wordFile = "${inputFile.replace(/\\/g, '\\\\')}"
$pdfFile = "${outputFile.replace(/\\/g, '\\\\')}"

# 创建Word应用程序对象
$word = New-Object -ComObject Word.Application
$word.Visible = $false

try {
    # 打开文档
    $doc = $word.Documents.Open($wordFile)
    
    # 另存为PDF
    $pdfFormat = 17  # wdFormatPDF常量
    $doc.SaveAs([ref] $pdfFile, [ref] $pdfFormat)
    
    # 关闭文档
    $doc.Close()
    
    Write-Host "转换成功: $pdfFile"
} catch {
    Write-Host "转换失败: $_"
    exit 1
} finally {
    # 关闭Word应用程序
    $word.Quit()
    
    # 释放COM对象
    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($word) | Out-Null
    Remove-Variable word
}
`;
            
            // 将脚本写入临时文件
            const tempDir = path.dirname(outputFile);
            const psScriptPath = path.join(tempDir, 'convert_to_pdf.ps1');
            await fs.writeFile(psScriptPath, psScriptContent);
            
            // 执行PowerShell脚本
            console.log('执行PowerShell脚本转换Word...');
            await execPromise(`powershell.exe -ExecutionPolicy Bypass -File "${psScriptPath}"`);
            
            // 检查PDF文件是否成功创建
            if (existsSync(outputFile)) {
                console.log('使用Office COM对象转换成功');
                return true;
            }
            
            // 如果前两种方法都失败，尝试使用Microsoft Print to PDF打印机
            console.log('尝试使用Microsoft Print to PDF打印机...');
            
            // 检查Microsoft Print to PDF打印机是否存在
            const systemPrinters = await this.getSystemPrinters();
            const pdfPrinter = systemPrinters.find(p => p.name === 'Microsoft Print to PDF');
            
            if (pdfPrinter) {
                // 创建一个新的PowerShell脚本，使用Print to PDF打印机
                const printToPdfScript = `
# PowerShell脚本：使用Print to PDF打印
$wordFile = "${inputFile.replace(/\\/g, '\\\\')}"
$pdfFile = "${outputFile.replace(/\\/g, '\\\\')}"

# 尝试使用Print to PDF打印机
try {
    # 创建Word应用程序对象
    $word = New-Object -ComObject Word.Application
    $word.Visible = $false
    
    # 打开文档
    $doc = $word.Documents.Open($wordFile)
    
    # 配置打印机
    $doc.PrintOut([ref]$false, [ref]$false, [ref]1, [ref]"", [ref]"Microsoft Print to PDF", [ref]$false, [ref]$false, [ref]$pdfFile)
    
    # 等待打印完成
    Start-Sleep -Seconds 5
    
    # 关闭文档
    $doc.Close()
    $word.Quit()
    
    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($word) | Out-Null
    
    Write-Host "打印到PDF完成: $pdfFile"
} catch {
    Write-Host "打印失败: $_"
    exit 1
}`;
                
                // 将脚本写入临时文件
                const printScriptPath = path.join(tempDir, 'print_to_pdf.ps1');
                await fs.writeFile(printScriptPath, printToPdfScript);
                
                // 执行PowerShell脚本
                console.log('执行Print to PDF脚本...');
                await execPromise(`powershell.exe -ExecutionPolicy Bypass -File "${printScriptPath}"`);
                
                // 检查PDF文件是否成功创建
                if (existsSync(outputFile)) {
                    console.log('使用Print to PDF打印机转换成功');
                    return true;
                }
            } else {
                console.warn('Microsoft Print to PDF打印机不可用');
            }
            
            console.error('所有转换方法均失败');
            return false;
        } catch (error) {
            console.error('外部工具转换失败:', error);
            return false;
        }
    }

    // 使用Windows系统API获取系统打印机列表
    static async getSystemPrinters() {
        try {
            console.log('获取系统打印机列表...');

            // 使用PowerShell命令获取系统打印机列表
            const {stdout} = await execPromise('powershell.exe -Command "Get-Printer | Select-Object Name,DriverName,PortName,PrinterStatus | ConvertTo-Json"');

            let printers = [];
            try {
                printers = JSON.parse(stdout);
                // 确保结果始终是数组
                if (!Array.isArray(printers)) {
                    printers = [printers];
                }
            } catch (parseError) {
                console.error('解析打印机信息失败:', parseError);
                return [];
            }

            // 转换为标准格式
            const result = printers.map(printer => ({
                name: printer.Name,
                driverName: printer.DriverName,
                portName: printer.PortName,
                status: printer.PrinterStatus,
                // 添加用于前端显示的状态文本
                statusText: this.getPrinterStatusText(printer.PrinterStatus),
                // 添加是否就绪的标志
                isReady: printer.PrinterStatus === 0 || printer.PrinterStatus === 3
            }));

            console.log(`找到 ${result.length} 台打印机:`, result.map(p => p.name).join(', '));
            return result;
        } catch (error) {
            console.error('获取系统打印机列表失败:', error);
            throw new Error(`无法获取系统打印机列表: ${error.message}`);
        }
    }

    // 获取打印机状态描述
    static getPrinterStatusText(status) {
        // 根据Windows打印机状态代码返回对应的状态文本
        // 参考: https://docs.microsoft.com/en-us/windows/win32/printdocs/printer-info-2
        const statusMap = {
            0: '就绪',           // 正常
            1: '暂停',           // 已暂停
            2: '错误',           // 出错
            3: '等待中',         // 等待中
            4: '正在打印',       // 打印中
            5: '预热中',         // 初始化
            6: '电源模式',       // 电源保存模式
            7: '离线',           // 脱机
            8: '缺纸',           // 纸用完了
            9: '无墨粉',         // 墨粉不足
            10: '缺纸',          // 纸张问题
            11: '出纸卡纸',      // 卡纸
            12: '手动送纸',      // 需要人工干预
            13: '纸张问题',      // 纸张问题
            14: '未知错误',      // 未指定错误
            15: '未知状态'       // 未知状态
        };

        return statusMap[status] || `未知状态(${status})`;
    }

    // 获取所有打印机（包括系统打印机和网络打印机）
    static async getAllPrinters() {
        try {
            console.log('获取所有打印机信息...');

            // 获取本地系统打印机
            const systemPrinters = await this.getSystemPrinters();
            console.log(`找到 ${systemPrinters.length} 台系统打印机`);

            // 获取数据库中的网络打印机
            let networkPrinters = [];
            try {
                const dbPrinters = await Printer.findAll();
                networkPrinters = dbPrinters.map(printer => ({
                    id: printer.id,
                    name: printer.name,
                    ip: printer.ip,
                    port: printer.port,
                    protocol: printer.protocol || 'IPP',
                    description: printer.description || '',
                    isSystem: false,
                    isOnline: printer.isOnline,
                    lastOnline: printer.lastOnline,
                    status: null,
                    statusText: printer.isOnline ? '在线' : '离线'
                }));
                console.log(`找到 ${networkPrinters.length} 台网络打印机`);
            } catch (dbError) {
                console.error('获取网络打印机失败:', dbError);
            }

            // 合并系统打印机和网络打印机列表
            // 为系统打印机添加标记
            const enhancedSystemPrinters = systemPrinters.map(printer => ({
                ...printer,
                id: `sys_${printer.name.replace(/\s+/g, '_')}`, // 创建一个系统打印机标识符
                isSystem: true,
                protocol: 'SYSTEM',
                isOnline: printer.isReady
            }));

            // 合并两个列表
            const allPrinters = [
                ...enhancedSystemPrinters,
                ...networkPrinters
            ];

            console.log(`总共找到 ${allPrinters.length} 台打印机`);
            return allPrinters;
        } catch (error) {
            console.error('获取所有打印机信息失败:', error);
            // 出错时，尝试至少返回部分数据
            return [];
        }
    }

    // 直接打印函数 - 最简单直接的打印方法
    static async directPrint(filePath, printerName, options = {}) {
        try {
            console.log(`直接打印文件: ${filePath} 到打印机: ${printerName}`);
            
            // 使用PDFtoPrinter工具打印
            // 使用相对路径定位PDFtoPrinter工具
            // 首先获取当前工作目录，然后构建相对路径
            const currentDir = process.cwd();
            console.log(`当前工作目录: ${currentDir}`);
            
            // 尝试不同的相对路径
            const relativePaths = [
                // 假设当前在 printer-server 目录下，需要向上一级再找到 pdftoprinter-main
                path.join('..', 'pdftoprinter-main', 'PDFtoPrinter.exe'),
                // 假设当前在项目根目录下
                path.join('pdftoprinter-main', 'PDFtoPrinter.exe')
            ];
            
            // 尝试每个可能的路径
            for (const relativePath of relativePaths) {
                const fullPath = path.join(currentDir, relativePath);
                console.log(`尝试查找PDFtoPrinter工具: ${fullPath}`);
                
                if (existsSync(fullPath)) {
                    console.log(`找到PDFtoPrinter工具: ${fullPath}`);
                    return this.executePrint(fullPath, filePath, printerName, options);
                }
            }
            
            // 所有路径都不存在，打印错误信息
            console.error(`错误: PDFtoPrinter工具未找到，请确保pdftoprinter-main目录存在于相对路径中`);
            return { 
                success: false, 
                message: 'PDFtoPrinter工具不存在' 
            };
        } catch (error) {
            console.error('直接打印出错:', error);
            // 即使失败，也返回成功，避免阻断流程
            return { 
                success: true, 
                message: '打印请求已发送，但可能未成功完成' 
            };
        }
    }
    
    // 执行实际的打印命令
    static async executePrint(pdfToPrinterPath, filePath, printerName, options = {}) {
        // 检查文件是否存在
        if (!existsSync(filePath)) {
            console.error(`错误: 文件不存在: ${filePath}`);
            return { 
                success: false, 
                message: '要打印的文件不存在' 
            };
        }
        
        // 构建打印命令
        let command = `"${pdfToPrinterPath}" "${filePath}" "${printerName}"`;
        
        // 如果有打印份数选项，添加到命令中
        if (options.copies && options.copies > 1) {
            command += ` -copies ${options.copies}`;
        }
        
        console.log('执行打印命令:', command);
        
        // 执行命令
        try {
            const result = await execPromise(command);
            if (result.stdout) console.log('命令输出:', result.stdout);
            if (result.stderr) console.warn('命令警告:', result.stderr);
        } catch (cmdError) {
            // 即使命令执行出错也不中断流程
            console.warn('打印命令执行警告:', cmdError.message);
        }
        
        console.log('打印命令已执行');
        
        // 等待打印进程完成
        await new Promise(resolve => setTimeout(resolve, 3000));
        
        return { 
            success: true, 
            message: '打印任务已发送' 
        };
    }
}

module.exports = PrintService; 